package com.example.animdemo.rigidanim;

import android.annotation.SuppressLint;
import android.util.FloatMath;

@SuppressLint("FloatMath")
public class RigidBody {
	public final RigidJoint[] skeleton;
	public final RigidJointInfo[] baseFrame;
	
	private RigidSoul soul;
	
	private float time;
	
	public float getTimeLife() {
		return soul==null?-1:soul.timeLife;
	}
	
	public float getTime()
	{
		return time;
	}
	
	public RigidBody(RigidJoint[] skeleton) {
		this.skeleton = skeleton;
		this.baseFrame = new RigidJointInfo[skeleton.length];
		for(int i = 0; i < skeleton.length; i++) {
			baseFrame[i] = new RigidJointInfo(skeleton[i].jointInfo);
		}
	}
	
	// Set animation to Model
    public void setSoul(RigidSoul soul)
    {
        this.soul = soul;
        for(int i = 0; i < baseFrame.length; i++) {
        	baseFrame[i].set(soul.frames[i][0]);
        }
        resetAnim();
    }

    // Reset Model to base frame
    public void resetAnim()
    {
    	time = 0;
//        for (int i = 0; i < skeleton.length; i++)
//            resetJoint(skeleton[i]);
    }
    
    private void resetJoint(RigidJoint joint)
    {
        joint.frameIndex = 0;
        joint.jointInfo.set(baseFrame[joint.jointId]);
        transformToWorld(joint, skeleton);
    }
    
    // this method only used if update all hierachy tree from root to leaf level by level.
    private static final void transformToWorld(RigidJoint joint, RigidJoint[] skeleton)
    {
        // calculate position base on rotation and length and the axit X unit vector.
        joint.wRot = joint.jointInfo.rot + joint.jointInfo.selfRot;
        float wPosX = joint.jointInfo.posX;
        float wPosY = joint.jointInfo.posY;

        if (joint.parentId != -1)
        {
        	RigidJoint parent = skeleton[joint.parentId]; 
            joint.wRot += parent.wRot;
            final float rad = parent.wRot + joint.jointInfo.rot;
            if (rad != 0)
            {
                final float s = FloatMath.sin(rad);
                final float c = FloatMath.cos(rad);
                final float x = wPosX;
                final float y = wPosY;
                wPosX = c * x - s * y;
                wPosY = s * x + c * y;
            }
            wPosX += parent.wPosX;
            wPosY += parent.wPosY;
        }
        else
        {
        	final float rad =  joint.jointInfo.rot;
        	if (rad != 0)
            {
                final float s = FloatMath.sin(rad);
                final float c = FloatMath.cos(rad);
                final float x = wPosX;
                final float y = wPosY;
                wPosX = c * x - s * y;
                wPosY = s * x + c * y;
            }
        }

        joint.wPosX = wPosX;
        joint.wPosY = wPosY;
    }

    public final void setTime(float time) {
		if (soul != null) {
			if (time > soul.timeLife)
				time = soul.timeLife;
			else if (time < 0)
				time = 0;
			animate(time - this.time);
		}
	}
    
	public final void animate(float dt) {
		if (soul == null || (dt > 0 && time == soul.timeLife) || (dt < 0 && time == 0))
			return;
		
		time += dt;
		
		// Check for stop animate Model.
		if (time > soul.timeLife) time = soul.timeLife;
		else if(time < 0) time = 0;
				
		final RigidJointInfo[][] frames = soul.frames;
		for (int i = 0; i < skeleton.length; i++) {
			// calculate frame, time.
			RigidJoint joint = skeleton[i];
			if (frames[i].length == 1) {
				joint.jointInfo.set(frames[i][0]);
				transformToWorld(joint, skeleton);
				continue;
			} else if (frames[i].length == 0)
				continue;

			// check for stop animate this joint
			float jtime = time;
			final float maxtime = frames[i][frames[i].length - 1].time;
			final float mintime = frames[i][0].time;
			// fix valid jtime.
			if(jtime < mintime) jtime = mintime;
			else if(jtime > maxtime) jtime = maxtime;
			// find currentID
			int currFrameIdx = joint.frameIndex;
			int numFrame = frames[i].length;
			
			// if jtime is smaller currentframe time.
			while(currFrameIdx > 0 && jtime < frames[i][currFrameIdx].time)
				currFrameIdx --;
			// if jtime is larger nextframe time.
			while(currFrameIdx < numFrame - 2 && jtime > frames[i][currFrameIdx + 1].time)
				currFrameIdx ++;
			
			joint.frameIndex = currFrameIdx;
			joint.jointInfo.time = jtime;
			// animate joint
			joint.jointInfo.slerp(frames[i][joint.frameIndex],
					frames[i][joint.frameIndex + 1]);
			transformToWorld(joint, skeleton);
		}
	}
}
